﻿// -- MVC4WPF
// -- Copyright © 2009 Information Control Corporation
// -- Revision History:
// -- Version 0.9.0.0 First codeplex release  - Stephen Giffin

using System;
using System.Windows;
using System.Windows.Threading;
using System.ComponentModel;

namespace MVC4WPF
{
    /// <summary>
    /// Base class that implements all the logic for event handling, properties, logic for methods exposed by IWPFViewWindow. 
    /// </summary>
    public class WPFViewWindow : Window, IWPFViewWindow
    {
        #region Constructor

        /// <summary>
        /// Base constructor that will register subscriptions to events on the base window clase. 
        /// This will also implement all the base methods.
        /// </summary>
        public WPFViewWindow()
        {
            try
            {
                //Subscribing to window events that are tracked and handled by this base class
                this.RegisterWindowClosed();
                this.RegisterWindowClosing();

            }
            catch
            {
                //Swallowing exceptions during construction
            }

        }
        #endregion

        #region IWPFViewWindow Members

        #region Implementation of event - ViewClosing

        /// <summary>
        /// Local variable that is the event handler for the event 'ViewClosing'.
        /// </summary>
        private CancelEventHandler _viewClosing;

        /// <summary>
        /// Local variable that is the lock object for the event 'ViewClosing', this is included for thread safety.
        /// </summary>
        private object _viewclosingEventLock = new object();

        /// <summary>
        /// Event that is Ond with the view is being closed. You can cancel the closing in the CancelEventArgs.
        /// </summary>
        event CancelEventHandler IWPFViewWindow.ViewClosing
        {
            add
            {
                lock (_viewclosingEventLock)
                {
                    _viewClosing += value;
                }
            }

            remove
            {
                lock (_viewclosingEventLock)
                {
                    _viewClosing -= value;
                }
            }
        }

        #endregion

        #region Implementation of event - ViewClosed

        /// <summary>
        /// Local variable that is the event handler for the event 'ViewClosed'.
        /// </summary>
        private EventHandler<EventArgs> _viewClosed;

        /// <summary>
        /// Local variable that is the lock object for the event 'ViewClosed', this is included for thread safety.
        /// </summary>
        private object _viewClosedEventLock = new object();

        /// <summary>
        /// Event that gets Ond with the view has closed
        /// </summary>
        event EventHandler<EventArgs> IWPFViewWindow.ViewClosed
        {
            add
            {
                lock (_viewClosedEventLock)
                {
                    _viewClosed += value;
                }
            }

            remove
            {
                lock (_viewClosedEventLock)
                {
                    _viewClosed -= value;
                }
            }
        }

        #endregion

        /// <summary>
        /// Shows the view in a none dialog mode.
        /// </summary>
        void IWPFViewWindow.ShowView()
        {
            ShowView();
        }

        /// <summary>
        /// Shows the view in a modal dialog mode in relation to the parent window. If no parent window is found this will not execute.
        /// </summary>
        /// <param name="parentWindow">The Window that owns this view.</param>
        void IWPFViewWindow.ShowViewModal(Window parentWindow)
        {
            ShowViewModal(parentWindow);
        }

        /// <summary>
        /// Hides the view.
        /// </summary>
        void IWPFViewWindow.HideView()
        {
            HideView();
        }

        #endregion

        #region Helper methods for IWPFViewWindow

        #region Implementation of ViewClosing event Handler
        /// <summary>
        /// Helper method that will On the 'ViewClosing' event if there are any subscribers. This method will invoke the event with a standard invoke.
        /// </summary>
        /// <param name="sender">The source caller of the event.</param>
        /// <param name="e">The event arguments to be Ond with the event.</param>
        protected void OnViewClosingEvent(object sender, CancelEventArgs e)
        {
            if (_viewClosing != null)
            {
                _viewClosing.Invoke(sender, e);
            }
        }

        /// <summary>
        /// Helper method that will release the closing event handler and release all subscribers from the event.
        /// </summary>
        private void ReleaseViewClosingEvent()
        {
            _viewClosing = null;

            _viewclosingEventLock = null;
        }

        /// <summary>
        /// Helper method that registers a listener for the window Closing Event.
        /// </summary>
        private void RegisterWindowClosing()
        {
            base.Closing += new CancelEventHandler(WPFViewWindow_Closing);
        }

        /// <summary>
        /// Event listener that will inform controllers of when the view begins closing.
        /// </summary>
        /// <param name="sender">The window that is closing.</param>
        /// <param name="e">The cancel event args that allows you to override the closing event.</param>
        protected virtual void WPFViewWindow_Closing(object sender, CancelEventArgs e)
        {
            OnViewClosingEvent(sender, e);

            if (!e.Cancel)
            {
                ReleaseViewWindowResources();
            }
        }

        #endregion

        #region Implementation of ViewClosed event Handler

        /// <summary>
        /// Helper method that will send the 'ViewClosed' event if there are any subscribers. This method will invoke the event with a standard invoke.
        /// </summary>
        /// <param name="sender">The source caller of the event.</param>
        /// <param name="e">The event arguments to be Ond with the event.</param>
        protected void OnViewClosedEvent(object sender, EventArgs e)
        {

            if (_viewClosed != null)
            {
                _viewClosed.Invoke(sender, e);
            }
        }

        /// <summary>
        /// Helper method that is to be called  to release the event 'ViewClosed' and supporting resources.
        /// </summary>
        private void ReleaseViewClosedEvent()
        {
            _viewClosed = null;

            _viewClosedEventLock = null;
        }

        /// <summary>
        /// Helper method that registers the base windows closed event to the WPFViewWindow_Closed method.
        /// </summary>
        private void RegisterWindowClosed()
        {
            base.Closed += new EventHandler(WPFViewWindow_Closed);
        }

        /// <summary>
        /// Event handler that handles when the window has closed. Will by default On the view closed event to subscribing controllers.
        /// </summary>
        /// <param name="sender">The window that has closed</param>
        /// <param name="e">Event arguments for the closed event.</param>
        protected virtual void WPFViewWindow_Closed(object sender, EventArgs e)
        {
            OnViewClosedEvent(sender, e);
        }

        #endregion

        /// <summary>
        /// Hides the view.
        /// This method can be overridden and a custome implementation can be added.
        /// </summary>
        protected virtual void HideView()
        {
            base.Hide();
        }

        /// <summary>
        /// Shows the view in a modal dialog mode in relation to the parent window. If no parent window is found this will not execute.
        /// This method can be overridden and a custome implementation can be added.
        /// </summary>
        /// <param name="parentWindow">The Window that owns this view.</param>
        protected virtual void ShowViewModal(Window parentWindow)
        {
            if (parentWindow != null)
            {
                base.Owner = parentWindow;
                base.ShowDialog();
            }
        }

        /// <summary>
        /// Shows the view in a none dialog mode. This method can be overridden and a custome implementation can be added.
        /// </summary>
        protected virtual void ShowView()
        {
            base.Show();
        }

        #endregion

        #region IWPFView Members

        /// <summary>
        /// Returns the current Dispatcher for this view.
        /// </summary>
        Dispatcher IWPFView.ViewDispatcher
        {
            get { return base.Dispatcher; }
        }

        /// <summary>
        /// Similar to the constructor of the view, 
        /// this helper method is used to set the view into a default state. 
        /// This is generally used when you want to reset the view, 
        /// or when you need to do additional configuration after supported data has been loaded.
        /// The hosting controller usally calls this method.
        /// </summary>
        void IWPFView.ConfigureView()
        {
            ConfigureView();
        }

        #endregion

        #region IWPFView helper methods

        /// <summary>
        /// Similar to the constructor of the view, 
        /// this helper method is used to set the view into a default state. 
        /// This is generally used when you want to reset the view, 
        /// or when you need to do additional configuration after supported data has been loaded.
        /// </summary>
        /// <remarks>
        /// This method is implemented with no functionality by default. 
        /// You can overload this method and add configuration functionality.
        /// </remarks>
        protected virtual void ConfigureView()
        {
            //Intentinally Blank
        }

        #endregion

        #region Implemention of Interface - INotifyPropertyChanged

        #region Implementation of event - PropertyChanged

        /// <summary>
        /// Local variable that is the event handler for the event 'PropertyChanged'.
        /// </summary>
        private PropertyChangedEventHandler _propertyChanged;

        /// <summary>
        /// Local variable that is the lock object for the event 'PropertyChanged', this is included for thread safety.
        /// </summary>
        private object _propertyChangedEventLock = new object();

        /// <summary>
        /// Subscribers to this event will be notified when a property has been changed, and the name of the changed property.
        /// </summary>
        event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
        {
            add
            {
                lock (_propertyChangedEventLock)
                {
                    _propertyChanged += value;
                }
            }

            remove
            {
                if (_propertyChangedEventLock != null)
                {
                    lock (_propertyChangedEventLock)
                    {
                        _propertyChanged -= value;
                    }
                }
            }
        }

        #endregion

        #region Helper methods for PropertyChanged

        /// <summary>
        /// Helper method that will On the 'PropertyChanged' event if there are any subscribers. This method will invoke the event with a standard invoke.
        /// </summary>
        /// <param name="sender">The source caller of the event.</param>
        /// <param name="e">The event arguments to be Ond with the event.</param>
        protected void OnPropertyChangedEvent(object sender, PropertyChangedEventArgs e)
        {

            if (_propertyChanged != null)
            {
                _propertyChanged.Invoke(sender, e);
            }
        }

        /// <summary>
        /// Helper method that will release the resources for the event "PropertyChanged"
        /// </summary>
        private void ReleasePropertyChangedEvent()
        {
            _propertyChanged = null;
            _propertyChangedEventLock = null;
        }

        /// <summary>
        /// This will alert all subscribers to the property changed event that a property has changed.
        /// </summary>
        /// <param name="propertyName">Name of the property that has changed.</param>
        protected void OnPropertyChanged(string propertyName)
        {
            this.OnPropertyChangedEvent(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion

        #endregion

        #region Implementation of Interface - INotifyPropertyChanging

        #region Implementation of event - PropertyChanging

        /// <summary>
        /// Local variable that is the event handler for the event 'PropertyChanging'.
        /// </summary>
        private PropertyChangingEventHandler _propertyChanging;

        /// <summary>
        /// Local variable that is the lock object for the event 'PropertyChanging', this is included for thread safety.
        /// </summary>
        private object _propertyChangingEventLock = new object();

        /// <summary>
        /// Subscribers to this event will be notified when a property is about to be changed, and the name of the changing property. 
        /// This is not a cancable event.
        /// </summary>
        event PropertyChangingEventHandler INotifyPropertyChanging.PropertyChanging
        {
            add
            {
                lock (_propertyChangingEventLock)
                {
                    _propertyChanging += value;
                }
            }

            remove
            {
                if (_propertyChangingEventLock != null)
                {
                    lock (_propertyChangingEventLock)
                    {
                        _propertyChanging -= value;
                    }
                }
            }
        }

        #endregion

        #region Helper methods for PropertyChanging

        /// <summary>
        /// Helper method that will On the 'PropertyChanging' event if there are any subscribers. This method will invoke the event with a standard invoke.
        /// </summary>
        /// <param name="sender">The source caller of the event.</param>
        /// <param name="e">The event arguments to be Ond with the event.</param>
        protected void OnPropertyChangingEvent(object sender, PropertyChangingEventArgs e)
        {
            if (_propertyChanging != null)
            {
                _propertyChanging.Invoke(sender, e);
            }
        }

        /// <summary>
        /// Helper method that will release all resources used for the PropertyChanging event.
        /// </summary>
        private void ReleasePropertyChangingEvent()
        {
            _propertyChanging = null;
            _propertyChangingEventLock = null;
        }

        /// <summary>
        /// This will alert all subscribers to the PropertyChanging event that a property is about to change.
        /// </summary>
        /// <param name="propertyName">The name of the property that is about to change.</param>
        protected void OnPropertyChanging(string propertyName)
        {
            this.OnPropertyChangingEvent(this, new PropertyChangingEventArgs(propertyName));
        }

        #endregion

        #endregion

        #region ResourceManager

        /// <summary>
        /// Releases all resources used by the ViewWindow base class.
        /// </summary>
        protected void ReleaseViewWindowResources()
        {

            //Releasing the views that this class subscribes to
            base.Closed -= this.WPFViewWindow_Closed;
            base.Closing -= this.WPFViewWindow_Closing;

            //Releasing the event handlers maintained by this class.
            ReleasePropertyChangedEvent();
            ReleasePropertyChangingEvent();
            ReleaseViewClosedEvent();
            ReleaseViewClosingEvent();
        }

        #endregion
    }
}
